<pre>
<script>
    class UpgradeRegexp{
        constructor(source,root = "root",flag){
            this.table = new Map();
            this.regexp = new RegExp(this.compileRegExp(source,root,0).source,flag);
        }
        
        exec(string){
            let r = this.regexp.exec(string)
            for (let index = 0; index < r.length; index++) {
                const element = r[index];
                if (element!==(void 0)) {
                    console.log(JSON.stringify(this.table.get(index-1)));
                    r[this.table.get(index-1)] = r[index];
                }
            }
            console.log(JSON.stringify(r[0]));
            return r;
        }

        compileRegExp(upgradeRegexp,name,start){
            let length = 0;
            if (upgradeRegexp[name] instanceof RegExp) {
                return {
                    source: upgradeRegexp[name].source,
                    length
                }
            }
            let regexp = upgradeRegexp[name].replace(/\<([^>]+)\>/g,(str,$1)=>{
                this.table.set(start+length,$1);
                ++length;

                let r = this.compileRegExp(upgradeRegexp,$1,start+length);
                length += r.length;
                return "("+r.source+")";
            });
            return {
                source:regexp,
                length,
            }
        }

        get lastIndex(){
            return this.regexp.lastIndex;
        }
        set lastIndex(value){
            return this.regexp.lastIndex = value;
        }
    }
    
    let upgradeRegexp = {
        InputElement:"<Whitespace>|<LineTerminator>|<Comments>|<Token>",
        Whitespace:/ /,
        LineTerminator:/\n/,
        Comments:/\/\*(?:[^*]|\*[^\/])*\*\/|\/\/[^\n]*/,
        Token:"<Literal>|<Keywords>|<IdentiferName>|<Punctuator>",
        Literal:"<NumericLiteral>|<BooleanLiteral>|<StringLiteral>|<NullLiteral>",
        NumericLiteral:/(?:[1-9][0-9]*|0)(?:\.[0-9]*)?/, 
        BooleanLiteral:/true|false/,
        StringLiteral:/\"(?:[^"\n]|\\[\s\S])*\"|\'(?:[^"\n]|\\[\s\S])*\'/,
        NullLiteral:/null/,
        IdentiferName:/[a-zA-Z_$][a-zA-Z0-9_$]*/,
        Keywords:/if|else|for|function|let|var/,
        Punctuator:/\(|=|<|\+\+|==|=>|\)|\*|\.|\[|\]|\{|\}|\.|\?|\:|;|\,|\+/
    }

    
    function scan(str){
        let regexp = new UpgradeRegexp(upgradeRegexp,"InputElement","g")
        while (regexp.lastIndex < str.length && regexp.lastIndex !== -1) {
            let r = regexp.exec(str);
            console.log(r);
            if (!r[0].length) {
                break;
            }
        }
    }
    
    scan(`
        let array = [1,2,3];
        for (let index = 0; index < array.length; index++) {
            const element = array[index];
            if (element===2) {
                console.log(element);
            } else {
                console.log(element);
            }
        }
    `)
</script>
</pre>